﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.IO;

namespace PulseApp
{
    public class TimeSeries
    {
        public TimeSeries()
        {
            labels = new ArrayList();
            timeReadings = new ArrayList();
            tsArray = new ArrayList();
        }

        public TimeSeries(int numOfDimensions)
        {
            labels = new ArrayList();
            timeReadings = new ArrayList();
            tsArray = new ArrayList();
            labels.Add("Time");
            for (int x = 0; x < numOfDimensions; x++)
                labels.Add("" + x);

        }

        public TimeSeries(TimeSeries origTS)
        {
            labels = new ArrayList(origTS.labels);
            timeReadings = new ArrayList(origTS.timeReadings);
            tsArray = new ArrayList(origTS.tsArray);
        }

        public TimeSeries(ArrayList tsArray)
        {
            this.tsArray = tsArray;
            timeReadings = new ArrayList();
            int i = 0;
            while (i < tsArray.Count)
            {
                timeReadings[i] = i;
                i++;
            }
        }
        
        /*public TimeSeries(string inputFile, int colToInclude[],
			    bool isFirstColTime, bool isLabeled, char delimiter) {
		    labels = new ArrayList();
            timeReadings = new ArrayList();
            tsArray = new ArrayList();
		    try {
                StreamReader br = new StreamReader(inputFile);
			    String line = br.ReadLine();
			    StringTokenizer st = new StringTokenizer(line, String
					    .valueOf(delimiter));
			    if (isLabeled) {
				f   or (int currentCol = 0; st.hasMoreTokens(); currentCol++) {
					    String currentToken = st.nextToken();
					    if (colToInclude.length == 0
							    || Arrays.contains(colToInclude, currentCol))
						    labels.add(currentToken);
				    }

				    if (labels.size() == 0)
					    throw new InternalError(
							    "ERROR:  The first row must contain label information, it is empty!");
				    if (!isFirstColTime)
					    labels.add(0, "Time");
				    else if (isFirstColTime
						    && !((String) labels.get(0)).equalsIgnoreCase("Time"))
					    throw new InternalError(
							    "ERROR:  The time column (1st col) in a time series must be labeled as 'Time', '"
									    + labels.get(0) + "' was found instead");
			    } else {
				i   f (colToInclude == null || colToInclude.length == 0) {
					    labels.add("Time");
					    if (isFirstColTime)
						    st.nextToken();
					    int currentCol = 1;
					    for (; st.hasMoreTokens(); labels.add(new String("c"
							    + currentCol++)))
						    st.nextToken();

				    } else {
					    java.util.Arrays.sort(colToInclude);
					    labels.add("Time");
					    for (int c = 0; c < colToInclude.length; c++)
						    if (colToInclude[c] > 0)
							    labels.add(new String("c" + c));

				    }
				    br.close();
				    br = new BufferedReader(new FileReader(inputFile));
			    }
			    do {
				    if ((line = br.readLine()) == null)
					    break;
				    if (line.length() > 0) {
					    st = new StringTokenizer(line, ",");
					    ArrayList<Double> currentLineValues = new ArrayList<Double>();
					    for (int currentCol = 0; st.hasMoreTokens(); currentCol++) {
						    String currentToken = st.nextToken();
						    if (colToInclude.length != 0
								    && !Arrays.contains(colToInclude, currentCol))
							    continue;
						    Double nextValue;
						    try {
							    nextValue = Double.valueOf(currentToken);
						    } catch (NumberFormatException e) {
							    throw new InternalError("ERROR:  '" + currentToken
									    + "' is not a valid number");
						    }
						    currentLineValues.add(nextValue);
					    }

					    if (isFirstColTime)
						    timeReadings.add(currentLineValues.get(0));
					    else
						    timeReadings.add(new Double(timeReadings.size()));
					    int firstMeasurement;
					    if (isFirstColTime)
						    firstMeasurement = 1;
					    else
						    firstMeasurement = 0;
					    TimeSeriesPoint readings = new TimeSeriesPoint(
							    currentLineValues.subList(firstMeasurement,
									    currentLineValues.size()));
					    tsArray.add(readings);
				    }
			    } while (true);
		    } catch (FileNotFoundException e) {
			    throw new InternalError("ERROR:  The file '" + inputFile
					    + "' was not found.");
		    } catch (IOException e) {
			    throw new InternalError("ERROR:  Problem reading the file '"
					    + inputFile + "'.");
		    }
        }

        public TimeSeries(String inputFile, int[] colToInclude,
			    bool isFirstColTime) {
		    this(inputFile, colToInclude, isFirstColTime, true, ',');
	    }

        public TimeSeries(String inputFile, bool isFirstColTime)
        {
            this(inputFile, ZERO_ARRAY, isFirstColTime);
        }*/

        public void clear()
        {
            labels.Clear();
            timeReadings.Clear();
            tsArray.Clear();
        }

        public int size()
        {
            return timeReadings.Count;
        }

        public int numOfPts()
        {
            return size();
        }

        public int numOfDimensions()
        {
            return labels.Count - 1;
        }

        public double getTimeAtNthPoint(int n)
        {
            return ((double)timeReadings[n]);
        }

        public string getLabel(int index)
        {
            return (string)labels[index];
        }

        public string[] getLabelsArr() {
            string[] labelArr = new string[labels.Count];
		    for (int x = 0; x < labels.Count; x++)
			    labelArr[x] = (string) labels[x];

		    return labelArr;
	    }

        public ArrayList getLabels()
        {
            return labels;
        }

        public void setLabels(string[] newLabels)
        {
		    labels.Clear();
		    for (int x = 0; x < newLabels.Length; x++)
			    labels.Add(newLabels[x]);

	    }

	    public void setLabels(ArrayList newLabels) {
		    labels.Clear();
		    for (int x = 0; x < newLabels.Count; x++)
			    labels.Add(newLabels[x]);

	    }

        public double getMeasurement(int pointIndex, int valueIndex)
        {
            return ((TimeSeriesPoint)tsArray[pointIndex]).get(valueIndex);
        }

        public double getMeasurement(int pointIndex, string valueLabel)
        {
            int valueIndex = labels.IndexOf(valueLabel);
            if (valueIndex < 0)
                throw new Exception("ERROR:  the label '" + valueLabel
                        + "' was " + "not one of:  " + labels);
            else
                return ((TimeSeriesPoint)tsArray[pointIndex])
                        .get(valueIndex - 1);
        }

        public double getMeasurement(double time, int valueIndex)
        {
            return 0.0D;
        }

        public double getMeasurement(double time, string valueLabel)
        {
            int valueIndex = labels.IndexOf(valueLabel);
            if (valueIndex < 0)
                throw new Exception("ERROR:  the label '" + valueLabel
                        + "' was " + "not one of:  " + labels);
            else
                return getMeasurement(time, valueIndex);
        }

        public double[] getMeasurementVector(int pointIndex)
        {
            return ((TimeSeriesPoint)tsArray[pointIndex]).toArray();
        }

        public double[] getMeasurementVector(double time)
        {
            return null;
        }

        public void setMeasurement(int pointIndex, int valueIndex, double newValue)
        {
            ((TimeSeriesPoint)tsArray[pointIndex]).set(valueIndex, newValue);
        }




        public void addFirst(double time, TimeSeriesPoint values)
        {
            if (labels.Count != values.size() + 1)
                throw new Exception("ERROR:  The TimeSeriesPoint: " + values
                        + " contains the wrong number of values. " + "expected:  "
                        + labels.Count + ", " + "found: " + values.size());
            if (time >= ((double)timeReadings[0]))
            {
                throw new Exception(
                        "ERROR:  The point being inserted into the beginning of the time series does not have the correct time sequence. ");
            }
            else
            {
                timeReadings.Insert(0, (double)time);
                tsArray.Insert(0, values);
                return;
            }
        }

        public void addLast(double time, TimeSeriesPoint values)
        {
            if (labels.Count != values.size() + 1)
                throw new Exception("ERROR:  The TimeSeriesPoint: " + values
                        + " contains the wrong number of values. " + "expected:  "
                        + labels.Count + ", " + "found: " + values.size());
            if (size() > 0
                    && time <= (double)(timeReadings[timeReadings.Count - 1]))
            {
                throw new Exception(
                        "ERROR:  The point being inserted at the end of the time series does not have the correct time sequence. ");
            }
            else
            {
                timeReadings.Add((double)(time));
                tsArray.Add(values);
                return;
            }
        }

        public void removeFirst()
        {
            if (size() == 0)
            {
                Console.Error.WriteLine("WARNING:  TimeSeriesPoint:removeFirst() called on an empty time series!");
            }
            else
            {
                timeReadings.Remove(0);
                tsArray.Remove(0);
            }
        }

        public void removeLast()
        {
            if (size() == 0)
            {
                Console.Error.WriteLine("WARNING:  TimeSeriesPoint:removeLast() called on an empty time series!");
            }
            else
            {
                timeReadings.Remove(timeReadings.Count - 1);
                tsArray.Remove(timeReadings.Count - 1);
            }
        }

        public void normalize() {
		    double[] mean = new double[numOfDimensions()];
		    for (int col = 0; col < numOfDimensions(); col++) {
			    double currentSum = 0.0D;
			    for (int row = 0; row < size(); row++)
				    currentSum += getMeasurement(row, col);

			    mean[col] = currentSum / (double) size();
		    }

            double[] stdDev = new double[numOfDimensions()];
		    for (int col = 0; col < numOfDimensions(); col++) {
			    double variance = 0.0D;
			    for (int row = 0; row < size(); row++)
				    variance += Math.Abs(getMeasurement(row, col) - mean[col]);

			    stdDev[col] = variance / (double) size();
		    }

		    for (int row = 0; row < size(); row++) {
			    for (int col = 0; col < numOfDimensions(); col++)
				    if (stdDev[col] == 0.0D)
					    setMeasurement(row, col, 0.0D);
				    else
					    setMeasurement(row, col,
							    (getMeasurement(row, col) - mean[col])
									    / stdDev[col]);

		    }

	    }

        public string toString()
        {
            StringBuilder outStr = new StringBuilder();
            for (int r = 0; r < timeReadings.Count; r++)
            {
                TimeSeriesPoint values = (TimeSeriesPoint)tsArray[r];
                for (int c = 0; c < values.size(); c++)
                    outStr.Append(values.get(c));

                if (r < timeReadings.Count - 1)
                    outStr.Append("\n");
            }

            return outStr.ToString();
        }

        protected void setMaxCapacity(int capacity)
        {
            timeReadings.Capacity = capacity;
            tsArray.Capacity = capacity;
        }











        private static int[] ZERO_ARRAY = new int[0];

        private  ArrayList labels;
        private  ArrayList timeReadings;
        private  ArrayList tsArray;
    }
}
